#include "common.h"
#include "../util/poll_set.hpp"                                                                                                                                                           
#include "../util/poll_manager.hpp"
#include "../transport/transport_kcp.hpp"
#include "../util/util.hpp"
#include "../kcp/Kcp.hpp"


//mmap
//#include <unistd.h>
//#include <sys/mman.h>

#include <thread>

#include <string>
#include <string.h>
#include <sys/stat.h>//struct stat

#include <boost/make_shared.hpp>
#include <boost/smart_ptr.hpp>
#include <functional>
#include <assert.h>
#include <memory>
#include <unistd.h>



#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>    
 
#define handle_error(msg) \
	do { perror(msg); exit(EXIT_FAILURE); } while(0)

//get sizeof of file , Bytes
size_t getFileSize(const char *fileName)
{
  if(fileName == NULL)
    return 0;
  struct stat statbuf;
  stat(fileName, &statbuf);
  size_t filesize = statbuf.st_size;
  return filesize;
}


int
main(int argc, const char *argv[]){

  
  /*-------------------------------------------------------------------------*/	
	FILE *fp;
	ssize_t send_size;
	char send_buf[1024+1]={0};
	//char send_buf[5120+1]={0};
	//char send_buf[10240+1]={0};
	int size_number=0;
  size_t file_size = 0;
	
	if(argc!=2){		
		fprintf(stderr,"Usage: %s <filename> \n",argv[0]);
		exit(EXIT_FAILURE);		
	}
	
	//if((fp = fopen(argv[1], "r+")) == NULL)
	if((fp = fopen(argv[1], "rb")) == NULL)
  { 
		handle_error("fopen");		
	}
  TRACE("file<", argv[1],  "> open successfully!\n");
  file_size = getFileSize(argv[1]); //Byte
	
  /*---------------------------------------------------------------------------*/



  /*---------------------------------------------------------------------------*/
  int clientfd;
  struct sockaddr_in serv_addr;
  memset(&serv_addr, 0, sizeof(serv_addr));
  if((clientfd = socket(AF_INET, SOCK_STREAM, 0))==-1)
  {
    std::cout<<"create socket failed : "<<strerror(errno)<<std::endl;
    return 0;
  }
  serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(6060);
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  if(connect(clientfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
  {
    std::cout<<"connect failed : "<<strerror(errno)<<std::endl;
    return 0;
  }

  char sdbuf[1024];
  char rvbuf[1024];
  int sendlen, recvlen = 0;
  sprintf(sdbuf, "Size:%ld Bytes", file_size);
  sendlen = write(clientfd, sdbuf, strlen(sdbuf));
  recvlen = read(clientfd, &rvbuf, sizeof(rvbuf));

  std::cout<<rvbuf<<std::endl;
  if(strncasecmp(rvbuf, "OK", 2)!=0)
  {
    std::cout<<"Send SIZE failed : "<<strerror(errno)<<std::endl;
    return 0;
  }

  //memset(sdbuf, 0, sizeof(sdbuf));

  shutdown(clientfd, SHUT_RDWR);
  close(clientfd);
  /*---------------------------------------------------------------------------*/

  /*---------------------------------------------------------------------------*/
  //create kcp_layer
  KcpOpt opt;
  opt.conv                = 666;
  opt.interval            = 5;
  opt.snd_wnd_size        = 1024;
  opt.rcv_wnd_size        = 2048;
  opt.nodelay             = true;
  //opt.use_congestion      = true;
  //SetControl(new CongestionControl());
  opt.trigger_fast_resend = 10;
  opt.offline_standard    = 20000;
  opt.snd_queue_max_size  = 5000;
  Kcp<true>::ptr kcp_layer  = std::make_shared<Kcp<true>>(opt);

  int sock_kcp;
  sock_kcp = socket(AF_INET, SOCK_DGRAM, 0);
  
  sockaddr_in remoteaddr;   std::string remotehost = "127.0.0.1";   int remoteport = 8080;
  sockaddr_in localaddr;    std::string localhost = "127.0.0.1";    int localport = 9090;

  remoteaddr.sin_family = AF_INET;                                                                                                            
  remoteaddr.sin_addr.s_addr = inet_addr(remotehost.c_str()); 
  remoteaddr.sin_port = htons(remoteport);

  localaddr.sin_family = AF_INET;                                                                                                            
  localaddr.sin_addr.s_addr = inet_addr(localhost.c_str()); 
  localaddr.sin_port = htons(localport);

  if(bind(sock_kcp, (sockaddr*)&localaddr, sizeof(localaddr))<0){INFO("sock_kcp bind failed.");}; 
  if(connect(sock_kcp, (sockaddr*)&remoteaddr, sizeof(remoteaddr))<0){INFO("sock_kcp connect failed.");};

    /*---------------------------------------------------------------------------*/
    
    usleep(1000000);

    char kcp_sdbuf[1024];
    char kcp_rvbuf[1024];
    int kcp_sendlen, kcp_recvlen = 0;
    sprintf(kcp_sdbuf, "HELLO KCP");
    kcp_sendlen = write(sock_kcp, kcp_sdbuf, strlen(kcp_sdbuf));
    if(kcp_sendlen<0){INFO("kcp write failed.");}

    kcp_recvlen = read(sock_kcp, &kcp_rvbuf, sizeof(kcp_rvbuf));
    if(kcp_recvlen<0){INFO("kcp read failed.");}
    std::cout<<kcp_rvbuf<<std::endl;
    if(strncasecmp(kcp_rvbuf, "HI KCP", 6)!=0)
    {
      std::cout<<"HELLOKCP failed : "<<strerror(errno)<<std::endl;
      return 0;
    }

    /*---------------------------------------------------------------------------*/

  const int flags = fcntl(sock_kcp, F_GETFL, 0); 
  if (flags < 0) {                                                                                                                                                                        
      return flags;
  }
  if (flags & O_NONBLOCK) {
      return 0;
  }
  fcntl(sock_kcp, F_SETFL, flags | O_NONBLOCK);


  kcp_layer->SetSendFunc([&](const void *buf, std::size_t size, void*)
                          {    
                            ssize_t len = sendto(sock_kcp, buf, size, 0,
                                                 (struct sockaddr*)&remoteaddr, sizeof(remoteaddr));
                            if(len == -1) //perror("sendto error");
                              INFO("[ERROR] sendto error");
                          });  


  bool shutting_down = false;
  std::thread thread_kcpupdate([&]
                          {
                            while(!shutting_down)
                            {
                              //INFO("KCP UPDATE");
                              if(kcp_layer->Update(iclock64()))
                              {
                                TRACE("UPDATE OFF LINE");
                              }
                              struct sockaddr_in client_addr; int addr_len;
                              uint8_t* buff_ptr = new uint8_t[1500];
                              int num_recv = recvfrom(sock_kcp, buff_ptr, 1500, 0, (struct sockaddr*)&client_addr, (socklen_t*)&addr_len);
                              //if(num_recv<0){INFO("recvfrom failed!");return ;}
                              if(num_recv>0){
                                kcp_layer->Input(buff_ptr, num_recv);
                                //kcp_layer->Flush();
                              }

                              delete [] buff_ptr;
                            }
                          });
  /*---------------------------------------------------------------------------*/

  int begintime, endtime;
  begintime = clock();
  /*---------------------------------------------------------------------------*/
  int recv_len = 0;

  while((recv_len=fread(send_buf, sizeof(char), sizeof(send_buf)-1, fp))>=0 && size_number < file_size)
  //while((recv_len=fread(send_buf, sizeof(send_buf), 1, fp))>=0 && size_number < file_size)
  {

    if(recv_len==0)
    {
      break;
    }


		//if(UDP_write(udp_transport, (unsigned char*)send_buf, recv_len)==false){
    if(kcp_layer->Send(send_buf, sizeof(send_buf))<0)
    {
      INFO("kcp_lay Send failed.");
			fclose(fp); 
			handle_error("send");		
      break;
		}
    kcp_layer->Flush();
    
		size_number=size_number+recv_len;
    procBar(size_number, file_size);
		usleep(1000);
		
		bzero(send_buf, sizeof(send_buf));	
	}
  
  

  endtime = clock();
  double proc_time = (double)(endtime - begintime)/(double)CLOCKS_PER_SEC;//sec
  double size_Mb = (((double)file_size*8)/(1024.0*1024.0));//Mbit
  double rate = size_Mb/proc_time;//Mbit/s
  printf("|:::::::::::::::::::::::::::|\n");
  printf("file size: %lf Mb\n", size_Mb);
  printf("use time: %lf s\n",proc_time);
  printf("rate: %lf Mbps\n", rate );
  printf("|:::::::::::::::::::::::::::|\n");

  printf("enter while(1)...\n");

  /*--------------------------------------------------------------------------------------*/

  while(1){}

  /*--------------------------------------------------------------------------------------*/
  shutting_down = true;
  thread_kcpupdate.join();

  close(sock_kcp);
  /*--------------------------------------------------------------------------------------*/

	fclose(fp);
	
	return 0;
}
